﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Nvigorate.Data.Query;
using Nvigorate.Extensions;
using Nvigorate.Relational.Mapping;
using Nvigorate.Relational.Mapping.Index;
using Nvigorate.Relational.Mapping.Interface;
using Nvigorate.Relational.Repository;
using Nvigorate.Relational.Visitors;
using Nvigorate.TestObjects;

namespace Nvigorate.Test.Relational.Repository
{
    [TestClass]
    public class DeleteVisitorTests
    {

        [TestMethod]
        public void Visit_WithNoParent_ReturnsASingleDeleteQuery()
        {
            // Arrange
            var repositoryMapIndex = CreateRepositoryMapIndex();
            AddMap(repositoryMapIndex, typeof(Person));

            var visitor = new DeleteVisitor(repositoryMapIndex.Object, false);
            var person = new Person {SocialSecurity = "123456789"};

            // Act
            visitor.Visit(PersistenceDecorator.Create(person), null);
            var query = visitor.GetQuery();

            // Assert
            Assert.AreEqual(1, query.Count);
            var delete = query[0] as Delete;
            Assert.IsNotNull(delete);
            Assert.AreEqual("Person", delete.Table.Name);
        }

        [TestMethod]
        public void Visit_WithParent_ReturnsADeleteForChildObject()
        {
            // Arrange
            var repositoryMapIndex = CreateRepositoryMapIndex();
            AddMap(repositoryMapIndex, typeof(Person));
            AddMap(repositoryMapIndex, typeof(Vehicle));

            var visitor = new DeleteVisitor(repositoryMapIndex.Object, false);
            var person = PersistenceDecorator.Create(new Person {SocialSecurity = "123456789"});
            var vehicle = PersistenceDecorator.Create(new Vehicle {VIN = "12345"});
            
            // Act
            visitor.Visit(vehicle, person);
            var query = visitor.GetQuery();

            // Assert
            Assert.AreEqual(1, query.Count);
            var delete = query[0] as Delete;
            Assert.IsNotNull(delete);
            Assert.AreEqual("Vehicle", delete.Table.Name);
        }

        [TestMethod]
        public void Direction_WhenRecursiveIsTrue_IsDepthFirst()
        {
            // Arrange
            var visitor = new DeleteVisitor(CreateRepositoryMapIndex().Object, true);

            // Assert
            Assert.AreEqual(TraversalDirection.DepthFirst, visitor.Direction);
        }

        [TestMethod]
        public void Direction_WhenRecursiveIsFalse_IsNoTraverse()
        {
            // Arrange
            var visitor = new DeleteVisitor(CreateRepositoryMapIndex().Object, false);

            // Assert
            Assert.AreEqual(TraversalDirection.NoTraversal, visitor.Direction);
        }

        #region Private Methods

        private IMock<IRepositoryMapIndex> CreateRepositoryMapIndex()
        {
            var mock = new Mock<IRepositoryMapIndex>();
            return mock;
        }

        private void AddMap(IMock<IRepositoryMapIndex> mock, Type type)
        {
            mock.Expect(m => m.CanDelete(type)).Returns(true);
            mock.Expect(m => m.HasMap(type)).Returns(true);
            mock.Expect(m => m.GetMap(type)).Returns(CreateBindingMap(type));
        }

        private IBindingMap CreateBindingMap(Type type)
        {
            var map = new BindingMap();
            ISourceMap sourceMap;

            if (type == typeof(Person))
            {
                sourceMap = new SourceMap("SocialSecurity") {SourceName = "Person"};
                sourceMap.Assignments.Add(
                    new MemberAssignment(true, "Person", "SocialSecurityNo", "Person", "SocialSecurity"));
                map.Sources.Add("SocialSecurityNo", sourceMap);
            }
            else if (type == typeof(Vehicle))
            {
                sourceMap = new SourceMap("VIN") {SourceName = "Vehicle"};
                sourceMap.Assignments.Add(
                    new MemberAssignment(true, "Vehicle", "VehicleId", "Vehicle", "VIN"));
                map.Sources.Add("VehicleId", sourceMap);
            }
            
            return map;
        }
        #endregion
    }
}
